﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;

using ERPStore.Extensions;
using ERPStore.Web.Extensions;
using ERPStore.QuoteCart.Repositories;

using CloudSoft.Extensions;

namespace ERPStore.QuoteCart.Services
{
	public class QuoteCartService
	{
		public QuoteCartService(Logging.ILogger logger
			, Repositories.IQuoteCartRepository repository
			, ERPStore.Services.ICatalogService catalogService
			, ERPStore.Services.IAccountService accountService
			, EventBrokR.IPublisher eventPublisherService
			, ERPStore.Services.IFraudService fraudService
			, ERPStore.Services.IOffersService offerService
			, ERPStore.Services.ISalesService salesService
			)
		{
			this.Logger = logger;
			this.CartRepository = repository;
			this.CatalogService = catalogService;
			this.AccountService = accountService;
			this.EventPublisherService = eventPublisherService;
			this.FraudService = fraudService;
			this.OfferService = offerService;
			this.SalesService = salesService;
		}

		protected Logging.ILogger Logger { get; private set; }
		protected Repositories.IQuoteCartRepository CartRepository { get; private set; }
		protected ERPStore.Services.ICatalogService CatalogService { get; private set; }
		protected ERPStore.Services.IAccountService AccountService { get; private set; }
		protected EventBrokR.IPublisher EventPublisherService { get; private set; }
		protected ERPStore.Services.IFraudService FraudService { get; private set; }
		protected ERPStore.Services.IOffersService OfferService { get; private set; }
		protected ERPStore.Services.ISalesService SalesService { get; private set; }

		public virtual Models.QuoteCart GetOrCreateQuoteCart(ERPStore.Models.UserPrincipal user)
		{
			if (user.VisitorId.IsNullOrTrimmedEmpty())
			{
				throw new ArgumentException("Visitor does not be null or empty");
			}
			var cart = CartRepository.GetQuoteCartList().GetByUserPrincipal(user).GetCurrent();
			if (cart != null)
			{
				return cart.ToQuoteCartModel();
			}
			var result = CreateQuoteCart(user);
			return result;
		}

		public virtual Models.QuoteCart GetCurrentQuoteCart(ERPStore.Models.UserPrincipal user)
		{
			var cart = CartRepository.GetQuoteCartList().GetByUserPrincipal(user).GetCurrent();
			return cart.ToQuoteCartModel();
		}

		public virtual Models.QuoteCart GetActiveCartById(string quoteCartCode)
		{
			var result = CartRepository.GetQuoteCartList().GetByCode(quoteCartCode).ToQuoteCartModel();
			return result;
		}

		public virtual void Clear(Models.QuoteCart cart)
		{
			if (cart == null)
			{
				return;
			}
			Logger.Info("Empty cart {0}", cart.Code);
			cart.Items.Clear();
		}

		public virtual void ChangeCurrentCart(string cartId, ERPStore.Models.UserPrincipal user)
		{
			ChangeCurrentCart(cartId, user.VisitorId);
		}

		public virtual void ChangeCurrentCart(string cartId, string visitorId)
		{
			CartRepository.ChangeCurrent(cartId, visitorId);
		}

		public virtual void AddCart(Models.QuoteCart cart, ERPStore.Models.UserPrincipal user)
		{
			CartRepository.Save(cart, user);
		}

		public virtual void Save(Models.QuoteCart cart, ERPStore.Models.UserPrincipal user)
		{
			if (cart == null)
			{
				return;
			}
			CartRepository.Save(cart, user);
		}

		public virtual Models.QuoteCart CreateQuoteCart(ERPStore.Models.UserPrincipal user)
		{
			if (user.VisitorId.IsNullOrTrimmedEmpty())
			{
				throw new ArgumentException("Visitor does not be null or empty");
			}

			var cart = new Models.QuoteCart();
			cart.VisitorId = user.VisitorId;
			cart.CartStateId = (int)Models.CartState.CurrentQuote;
			return cart;
		}

		public virtual Models.QuoteCart GetCartByCode(string quoteCartCode)
		{
			var cart = CartRepository.GetQuoteCartList().GetByCode(quoteCartCode);
			return cart.ToQuoteCartModel();
		}

		public virtual Models.QuoteCart CreateAndSaveQuoteCart(ERPStore.Models.UserPrincipal user)
		{
			var cart = CreateQuoteCart(user);
			CartRepository.Save(cart, user);
			return cart;
		}

		public virtual IList<Models.QuoteCart> GetCurrentQuoteList(ERPStore.Models.UserPrincipal user)
		{
			if (user.VisitorId.IsNullOrTrimmedEmpty())
			{
				throw new ArgumentException("Visitor does not be null or empty");
			}

			var list = from cart in CartRepository.GetQuoteCartList().GetByUserPrincipal(user).ToList()
					   where cart.CartStateId != (int)Models.CartState.ConvertedToQuote
					   select cart.ToQuoteCartModel();

			return list.ToList();
		}

		public Models.QuoteCart GetQuoteCartByConvertedEntityId(int entityId, ERPStore.Models.UserPrincipal user)
		{
			var cart = CartRepository.GetQuoteCartList().GetByUserPrincipal(user).Where(i => i.ConvertedEntityId == entityId).FirstOrDefault();
			return cart.ToQuoteCartModel();
		}

		public Models.QuoteCart ConvertToQuoteCart(ERPStore.Models.OrderCart orderCart, ERPStore.Models.UserPrincipal principal)
		{
			var quoteCart = CreateQuoteCart(principal);
			var user = principal.CurrentUser;
			return quoteCart;
		}

		public ERPStore.Models.OrderCart ConvertToOrderCart(Models.QuoteCart quoteCart, ERPStore.Models.UserPrincipal principal)
		{
			var cartService = GlobalConfiguration.Configuration.DependencyResolver.GetService<ERPStore.Services.ICartService>();
			var orderCart = cartService.CreateCart(principal);
			orderCart.CustomerDocumentReference = quoteCart.CustomerDocumentReference;
			orderCart.FromEntityId = quoteCart.Id;
			orderCart.Message = quoteCart.Message;
			foreach (var item in quoteCart.Items)
			{
				OfferService.ApplyOffers(item.Product, principal.CurrentUser);
				cartService.AddCartItem(orderCart, item.Product, item.Quantity, item.OfferName, "127.0.0.1", item.AddFrom);
			}
			orderCart.State = ERPStore.Models.CartState.OrderWaitingProcess;
			SalesService.CalculateShippingFee(orderCart, principal);
			cartService.Save(orderCart);
			return orderCart;
		}

		public virtual void AddItem(Models.QuoteCart cart, ERPStore.Models.Product product, int quantity, string offerName, string ip, string addFrom)
		{
			// TODO : Placer ces règles d'ajout dans SalesService
			// checker la possibilité d'ajout
			if (product.SaleMode == ERPStore.Models.ProductSaleMode.NotSellable)
			{
				// TODO : Convert to throw exception
				Logger.Warn("Add not sellable product {0} in order cart", product.Code);
				return;
			}

			string reason = null;
			if (FraudService.IsBlackListed(cart.VisitorId, ip, out reason))
			{
				Logger.Notification(reason);
				return;
			}

			quantity = Math.Max(1, quantity);
			var existing = cart.Items.FirstOrDefault(i => i.Product.Id == product.Id);
			var productStockInfo = CatalogService.GetProductStockInfo(product);

			if (product.SaleMode == ERPStore.Models.ProductSaleMode.EndOfLife)
			{
				if (productStockInfo == null)
				{
					// TODO : Convert to throw exception
					Logger.Warn("Add end of life product {0} in order cart", product.Code);
					return;
				}

				var q = quantity;
				if (existing != null)
				{
					q = existing.Quantity + quantity;
				}
				if (productStockInfo.AvailableStock < q)
				{
					// TODO : Convert to throw exception
					Logger.Warn("Add end of life product {0} in order cart , quantity {1}", product.Code, quantity);
					return;
				}
			}

			if (existing == null)
			{
				Logger.Info("Add product {0} quantity {1} cart {2}", product.Code, quantity, cart.Code);

				var cartItem = new Models.QuoteCartItem();
				cartItem.Product = product;
				cartItem.Quantity = quantity;
				cartItem.Packaging = product.Packaging.Value;
				cartItem.OfferName = offerName;
				cartItem.AddFrom = addFrom;
				if (productStockInfo == null)
				{
					cartItem.InventoryTimeStateId = -4; // Never in stock

				}
				else if (productStockInfo.AvailableStock > quantity)
				{
					cartItem.InventoryTimeStateId = -1; // Available
				}
				else if (productStockInfo.HasStock)
				{
					cartItem.InventoryTimeStateId = -2; // PArtialyAvailable
				}
				else
				{
					cartItem.InventoryTimeStateId = -3; // Not available
				}


				cart.Items.Add(cartItem);
			}
			else 
			{
				Logger.Info("Update cartitem product {0} quantity {1} cart {2}", product.Code, quantity, cart.Code);
				existing.Quantity += quantity;
			}
		}

		public virtual void RemoveItem(Models.QuoteCart cart, int index)
		{
			if (cart == null)
			{
				return;
			}
			if (index < 0 
				|| index >= cart.ItemCount)
			{
				return;
			}
			var item = cart.Items[index];
			Logger.Info("Delete cartitem product {0} cart {1}", item.Product.Code, cart.Code);
			cart.Items.Remove(item);
		}

		public virtual void DeleteCart(string cartId, ERPStore.Models.UserPrincipal user)
		{
			var cart = GetActiveCartById(cartId);

			if (cart == null)
			{
				return;
			}

			var currentQuoteCart = GetCurrentQuoteCart(user);
			if (currentQuoteCart != null && currentQuoteCart.Code.Equals(cartId, StringComparison.InvariantCultureIgnoreCase))
			{
				CartRepository.Remove(cartId);
			}
			else
			{
				var list = GetCurrentQuoteList(user);
				if (list.IsNotNullOrEmpty())
				{
					ChangeCurrentCart(list.First().Code, user);
				}
			}
			CartRepository.Remove(cartId);
		}
	}
}
